/**
 * 异步的服务端流协议类, 支持local流和tcp流, 依赖boost::asio库.
 * 每次连接创建一个socket连接类(由调用处提供类型, protocol要与server一致), 然后调用其start函数, 故提供的socket连接类必须: 
 *  1. 支持boost::asio::io_service引用以及int作为参数的构造, int类型作为一个flag型参数, 额外告之connection_type的处理策略
 *  2. 支持public的socket函数获取socket引用对象, 本类accept要获取存储连接信息, 如对端地址等
 *  3. 重载operator()操作符(无参), 作为新连接接受后处理入口
 *  4. 可选继承自boost::enable_shared_from_this(做为短连接且没有异步操作时则不用, start函数返回即析构) 
 * 
 * 开发者   : Marco
 * 创建时间 : 2016年7月29日, 下午1:53
 */

#ifndef __MARCO_BST_STREAM_SERVER_HPP__
#define __MARCO_BST_STREAM_SERVER_HPP__

#ifdef __cplusplus

#include <boost/asio/basic_socket_acceptor.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>

#include "stream_basic.h"

namespace marco {
namespace bst {

/**
 * 服务端类, 需要提供一个协议类型(tcp或local), 以及新连接的socket类型
 */
template <typename protocol, typename connection_type> class stream_server {
public:
    typedef typename boost::shared_ptr<connection_type> pointer;
    typedef typename protocol::endpoint endpoint_type;

    /**
     * tcp协议适用, 只指定端口, 默认绑定所有IP地址(即0.0.0.0)
     * @param io_service boost io服务对象
     * @param bind_port 绑定端口
     * @exception 绑定失败
     */
    explicit stream_server(boost::asio::io_service& io_service, int bind_port, bool defer_accept = false) throw (boost::system::system_error&)
	try : acceptor_(io_service, endpoint_type(boost::asio::ip::tcp::v4(), bind_port)) {
	
	acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));  // 地址重用

	if (defer_accept) {
	    int second = 10;    // 设置首次连接必须有数据, boost::asio不支持此选项, 使用原生api, 等待指定秒, 如果依然没有数据则accept返回.
	    setsockopt(acceptor_.native_handle(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &second, sizeof(second)); 
	}
	
	start_accept(); // 开始处理异步接入连接
	
    } catch (boost::system::system_error &err) {
	throw err;
    }
    
    /**
     * tcp协议适用, 指定绑定IP与端口
     * @param io_service boost io服务对象
     * @param bind_address 绑定地址,0.0.0.0表示绑定所有网卡IP, 127.0.0.1绑定本地
     * @param bind_port 绑定端口
     * @exception 绑定失败
     */
    explicit stream_server(boost::asio::io_service& io_service, const char* bind_address, 
			int bind_port, bool defer_accept = false) throw (boost::system::system_error&)
	try : acceptor_(io_service, endpoint_type(boost::asio::ip::address::from_string(bind_address), bind_port)) {
	acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));  // 地址重用

	if (defer_accept) {
	    int second = 10;    // 设置首次连接必须有数据, boost::asio不支持此选项, 使用原生api, 等待指定秒, 如果依然没有数据则accept返回.
	    setsockopt(acceptor_.native_handle(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &second, sizeof(second)); 
	}
	
	start_accept(); // 开始处理异步接入连接
	
    } catch (boost::system::system_error &err) {
	throw err;
    }
    
    /**
     * local协议适用 , 绑定本地路径
     * @param io_service boost::asio服务类
     * @param path 绑定文件路径, 该路径文件必须不存在, 否则出现已绑定错误.
     * @exception 绑定失败
     */
    explicit stream_server(boost::asio::io_service& io_service, const char* path) 
	throw (boost::system::system_error&) try : acceptor_(io_service, endpoint_type(path)) {
	start_accept();
    } catch (boost::system::system_error &err) {
	throw err;
    }
    
private:
    /**
     * 处理异步接入连接
     */
    inline void start_accept() {
	// 客户端对象, 使用shared_ptr包装
	pointer new_connection = boost::make_shared<connection_type>(acceptor_.get_io_service());

	// 异步接收连接, 新连接到来时,存为刚新建的socket对象, 回调函数为本类成员函数handle_accept, 并传入shared_ptr对象
	acceptor_.async_accept(new_connection->socket(), boost::bind(&stream_server::handle_accept, this,
		new_connection, boost::asio::placeholders::error));
    }

    /**
     * 异步连接成功回调函数
     * @param new_connection 新连接shared_ptr对象
     * @param error 错误信息
     */
    void handle_accept(pointer new_connection, const boost::system::error_code& error) {
	if (!error) {
	    (*new_connection)();	// 新连接类处理函数
	} else if (error == boost::asio::error::operation_aborted) { // 操作取消, 被析构
	    return;
	}

	start_accept(); // 再次创建socket实例,进入等待连接事件循环
    }

    stream_server(const stream_server& orig); // 禁用拷贝构造
    stream_server& operator=(const stream_server& orig); // 禁用赋值

#if (__cplusplus >= 201103L)
    stream_server(stream_server&& orig) = delete; // 移动构造禁用
    stream_server& operator=(stream_server&& orig) = delete; // 移动赋值运算符重载禁用
#endif

    boost::asio::basic_socket_acceptor<protocol> acceptor_;
};

} // namespace bst
} // namespace marco
#endif /* endif __cplusplus */

#endif /* __MARCO_BST_STREAM_SERVER_HPP__ */